/*
 * This file is part of algowala.
 *
 * algowala is copyright (c) 2012 Jagannathan Sampath <algowala@jagernot.com>
 *
 * algowala is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 2 of the License, or
 * (at your option) any later version.
 *
 * algowala is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
 * for more details.
 *
 * You should have received a copy of the GNU General Public License along
 * with algowala.  If not, see <http://www.gnu.org/licenses/>.
 *
*/

#include "solver.h"
#include "multi_curve.h"
#include "random.h"
#include "curve_editor.h"
#include "console.h"
extern console cons;

#include <cmath>
using namespace std;

void solver::init () {
  mcrv = 0;
  icurv = ncurvs = 0;
  iseg = 0;
  firstx = firsty = 0;
  lastx = lasty = 0;
  startx = starty = 0;
  endx = endy = 0;
  result = 0;

}

solver::solver () {
  init ();
}

solver::solver (multi_curve* c) {
  init ();
  operator() (c);
}

void solver::operator() (multi_curve* c) {
  mcrv = c;
  update ();
}

void solver::update () {
		
  // curve valid?
  vector<curve>& curv = mcrv->curv;
  ncurvs = curv.size ();
  last_curv = ncurvs - 1;
  if (icurv > last_curv) icurv = 0;

  // segment valid?
  vector<crvpt>& vpts = curv[icurv].vpts;
  int nsegs = vpts.size() - 1;
  if (iseg >= nsegs) iseg = 0;

  setseg (icurv, iseg);

  // first point
  vector<crvpt>& fpts = curv[0].vpts;
  firstx = fpts[0].x;
  firsty = fpts[0].y;

  // last point
  vector<crvpt>& lpts = curv[last_curv].vpts;
  int last_point = lpts.size () - 1;
  lastx = lpts[last_point].x;
  lasty = lpts[last_point].y;

}

// solves y for x

double solver::operator() (double x) {

  if (x < startx) {
    if (!searchleft (x)) {
      result = firsty;
      return result;
    }
  } else if (x > endx) {
    if (!searchright (x)) {
      result = lasty;
      return result;
    }
  }

  result = ycomp + m * x;
  return result;

}

double solver::operator() (double& x, double& dx, xhandler& xmin, xhandler& xmax) {

  if (x < startx) {
    if (!searchleft (x)) xmin (*this, x, dx);
  } else if (x > endx) {
    if (!searchright (x)) xmax (*this, x, dx);
  }

  double result = ycomp + m * x;
  x += dx;

  return result;

}

// solves y for x with modulation of x
void solver::operator() (double& x, double& dx, int q, double* mod, double* y, xhandler& xmin, xhandler& xmax) {
  for (int p = 0; p < q; ++p) {
    x += (mod[p] + dx);
    if (x < startx) {
      if (!searchleft (x)) xmin (*this, x, dx);
    } else if (x > endx) {
      if (!searchright (x)) xmax (*this, x, dx);
    }
    y[p] = ycomp + m * x;


  }
}

// solves y for x
void solver::operator() (double& x, double& dx, int q, double* y, xhandler& xmin, xhandler& xmax) {
  for (int p = 0; p < q; ++p) {
    x += dx;
    if (x < startx) {
      if (!searchleft (x)) xmin (*this, x, dx);
    } else if (x > endx) {
      if (!searchright (x)) xmax (*this, x, dx);
    }
    y[p] = ycomp + m * x;
  }
}

// solves array of x. stores solution ie y in the same array at corresponding location
void solver::operator() (double* ax, int q, xhandler& xmin, xhandler& xmax) {

  double x, dx = 0;
  for (int p = 0; p < q; ++p) {
    x = ax[p];
    if (x < startx) {
      if (!searchleft (x)) xmin (*this, x, dx);
    } else if (x > endx) {
      if (!searchright (x)) xmax (*this, x, dx);
    }
    ax[p] = ycomp + m * x;
  }

}

void atmin::operator() (solver& s, double& x, double& dx) {
  x = s.firstx;
  s.setseg (0, 0);

}

void atmax::operator () (solver& s, double& x, double& dx) {
  x = s.lastx;
  s.setseg (s.last_curv, s.lastseg (s.last_curv));
}

void tomin::operator() (solver& s, double& x, double& dx) {
  x -= s.lastx;
  if (!s.inseg (x, 0, 0)) if (!s.searchright(x)) if (!s.searchleft (x)) {
    x = s.firstx;
    s.setseg (0, 0);
  }
  
}

void loopmin::operator() (solver& s, double& x, double& dx) {
  atmin::operator() (s, x, dx);
  if (dx < 0) dx = -dx;
}

void loopmax::operator() (solver& s, double& x, double& dx) {
  tomin::operator() (s, x, dx);
  if (dx < 0) dx = -dx;
}

void pongmax::operator () (solver& s, double& x, double& dx) {
  atmax::operator() (s, x, dx);
  if (dx > 0) dx = -dx;
}

void pongmin::operator () (solver& s, double& x, double& dx) {
  atmin::operator() (s, x, dx);
  if (dx < 0) dx = -dx;
}
